//################################################################################
// MIT License
// Copyright (c) 2024 ZhangYihua
//
// Change Logs:
// Date           Author       Notes
// 2020-11-13     ZhangYihua   first version
//
// Description  : 
//################################################################################

module sync_apb #(
parameter           ADDR_BW                 = 16,
parameter           DATA_BW                 = 32,
parameter           SRC_INPUT_HOLD          = 1'b0,     // set 1'b1 to it, if paddr, pwrite or pwdata is not all registered
parameter           DST_INPUT_HOLD          = 1'b1,     // set 1'b0 to it, if prdata is registered
parameter           SYNC_NUM_D2S            = 3,
parameter           SYNC_NUM_S2D            = 3
) ( 
input                                       rst_src_n,
input                                       clk_src,

input                                       src_psel,
input                                       src_penable,    // not used here
input                                       src_pwrite,
input               [ADDR_BW-1:0]           src_paddr,
input               [DATA_BW-1:0]           src_pwdata,
output              [DATA_BW-1:0]           src_prdata,
output                                      src_pready,

input                                       rst_dst_n,
input                                       clk_dst,

output                                      dst_psel,
output  reg                                 dst_penable,
output                                      dst_pwrite,
output              [ADDR_BW-1:0]           dst_paddr,
output              [DATA_BW-1:0]           dst_pwdata,
input               [DATA_BW-1:0]           dst_prdata,
input                                       dst_pready
);

//################################################################################
// define local varialbe and localparam
//################################################################################
wire                                        src_req;
wire                [DATA_BW+ADDR_BW-1:0]   src_info;
wire                                        src_nfr;
wire                                        src_ack;
wire                [DATA_BW-1:0]           src_resp;
wire                                        dst_req;
wire                [DATA_BW+ADDR_BW-1:0]   dst_info;
wire                                        dst_nfr;
wire                                        dst_ack;
wire                [DATA_BW-1:0]           dst_resp;

//################################################################################
// main
//################################################################################

assign src_req  = src_psel;
assign src_nfr  = ~src_pwrite;
assign src_info = {src_paddr, src_pwdata};

assign src_pready = src_ack;
assign src_prdata = src_resp;

sync_req #(
        .INFO_BW                        (DATA_BW+ADDR_BW                ),
        .RESP_BW                        (DATA_BW                        ),
        .INFO_INPUT_HOLD                (SRC_INPUT_HOLD                 ),	// set it 1'b0 for area saving if src_info/src_nfr is REG and stable during src_req==1'b1
        .RESP_INPUT_HOLD                (DST_INPUT_HOLD                 ),	// set it 1'b0 for area saving if dst_resp is REG and stable until next dst_ack==1'b1
        .SYNC_NUM_D2S                   (SYNC_NUM_D2S                   ),
        .SYNC_NUM_S2D                   (SYNC_NUM_S2D                   )
) u_sync_req ( 
        .rst_src_n                      (rst_src_n                      ),
        .clk_src                        (clk_src                        ),

        .src_req                        (src_req                        ),	// keep src_req 1'b1 until src_ack, and back-to-back is allowed
        .src_info                       (src_info                       ),	// forward information, keep stable when src_req
        .src_nfr                        (src_nfr                        ),	// 1'b1: need for response; 1'b0 not need for response
        .src_ack                        (src_ack                        ),	// deassert src_req immediatelly after src_ack
        .src_resp                       (src_resp                       ),	// backward response, valid when src_ack, and stable until next src_ack==1'b1

        .rst_dst_n                      (rst_dst_n                      ),
        .clk_dst                        (clk_dst                        ),

        .dst_req                        (dst_req                        ),
        .dst_info                       (dst_info                       ),
        .dst_nfr                        (dst_nfr                        ),	// 1'b1: need for response; 1'b0 not need for response
        .dst_ack                        (dst_ack                        ),
        .dst_resp                       (dst_resp                       )
);

always@(posedge clk_dst or negedge rst_dst_n) begin
    if (rst_dst_n==1'b0) begin
        dst_penable <=`U_DLY 1'b0;
    end else begin
        if ((dst_psel==1'b1) && (dst_penable==1'b0))
            dst_penable <=`U_DLY 1'b1;
        else if (dst_ack==1'b1)
            dst_penable <=`U_DLY 1'b0;
        else
            ;
    end
end

assign dst_psel   = dst_req;
assign dst_pwrite = ~dst_nfr;
assign {dst_paddr, dst_pwdata} = dst_info;

assign dst_resp = dst_prdata;
assign dst_ack  = dst_penable & dst_pready;

//################################################################################
// ASSERTION
//################################################################################

`ifdef CBB_ASSERT_ON
// synopsys translate_off


// synopsys translate_on
`endif

endmodule
